package modelo;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import constants.Constants;

import grafos.AbstractGraph;
import grafos.UndirectedGraph;
/**
 * this is the construct class that will let us to control the enviroment 
 * of the board
 * @author Jose Luis Garcia && Alvaro Blazquez Checa
 *
 */

public class Board {
	private AbstractGraph<Square> boardMap;
	private List<Square> squareList;
	/**
	 * this method will read the board names and will create a 
	 * graph with the reachable rooms 
	 */
	public Board(){
		boardMap=new UndirectedGraph<Square>();
		squareList= new ArrayList<Square>();
		try {
			ReadFromXML reader = new ReadFromXML(Constants.MAP_FILE);
		} catch (ParserConfigurationException | SAXException | IOException | NumberFormatException e) {
			System.out.println("Error en el xml del mapa");
			System.out.println(e);
			System.exit(1);	
		}
	}
	/**
	 * this will return a square graph 
	 * @param num
	 * @return
	 */
	public Square getSquare(int num){
		for(Square s: squareList){
			if (s.getSquareNum() == num) return s;
		}
		return null;
	}
	/**
	 * return the square list of nodes 
	 * @return
	 */
	public List<Square> getSquareList(){
		return squareList;
	}
	/**
	 * this will add a square graph node
	 * @param square
	 * @return
	 */
	private boolean addSquare(Square square){
		if(boardMap.addVert(square)){
			squareList.add(square);
			return true;
		}
		else return false;
	}
	/**
	 * this will remove a square graph from the list 
	 * @param square
	 * @return
	 */
	private boolean removeSquare(Square square){
		if(boardMap.removeVert(square)){
			squareList.remove(square);
			return true;
		}
		else return false;
	}
	/**
	 * returns true is you can get to that square
	 * @param square
	 * @return
	 */
	public boolean isSquare(Square square){
		return boardMap.isVert(square);
	}
	/**
	 * this will link 2 square
	 * @param c1
	 * @param c2
	 * @return
	 */
	private boolean connectSquare(Square c1,Square c2){
		if(boardMap.addEdge(c1, c2)) return true;
		else return false;
	}
	/**
	 * this will link 2 square
	 * @param c1
	 * @param c2
	 * @return
	 */
	private boolean connectSquare(Square c1,List<Square> list){
		boolean ok=true;
		for(Square c : list) 
			if(!boardMap.addEdge(c1, c)) ok=false;
		return ok;
	}
	/**
	 * this will unlink 2 square
	 * @param c1
	 * @param c2
	 * @return
	 */
	private boolean disconnectSquare(Square c1,Square c2){
		if(boardMap.removeEdge(c1, c2))return true;
		else return false;
	}
	/**
	 * returns the distance from 2 squares
	 * @param c1
	 * @param distance
	 * @return
	 */
	public Set<Square> squaresConnectedTo(Square c1,int distance){
		return boardMap.getIncomingVertexOf(c1,distance);
	}
	/**
	 * this will returns the reachable square from a position
	 * @param c1
	 * @return
	 */
	public List<String> getReachableSquares(Square c1){
		List<String> list = new ArrayList<String>();
		Set<Square> squareSet = boardMap.getIncomingVertexOf(c1,Constants.MAX_MOVES);
		for(Square square: squareSet){
			list.add(square.toString());
		}
		return list;
	}
	/**
	 * 
	 * THIS IS THE SAX PARSER
	 * to load from xml
	 *
	 */
	private class ReadFromXML extends DefaultHandler{
		
		private SAXParserFactory spfac;
		SAXParser sp;
		Square sqrTmp;
		String tmpValue;
		Square origen;
		ArrayList<Square> listaCasillasDst;
		/**
		 * construct the vinculation between xml and java objects
		 * @param fichero
		 * @throws ParserConfigurationException
		 * @throws SAXException
		 * @throws IOException
		 */
		public ReadFromXML(String fichero) throws ParserConfigurationException, SAXException, IOException{
			spfac = SAXParserFactory.newInstance();
			sp = spfac.newSAXParser();
			sp.parse(fichero, this);
		}
		/**
		 * will create a objects if it reads his primary key
		 */
		public void startElement(String uri, String localName,
                String elementName, Attributes attributes) throws SAXException {
			if(elementName.equalsIgnoreCase("square")){
				sqrTmp = new Square();
			}
			if(elementName.equalsIgnoreCase("destiny")){
				listaCasillasDst = new ArrayList<Square>();
			}
		}
		/**
		 * will add content to the object readed before
		 */
		public void endElement(String s, String s1, String element) throws SAXException {
			if(element.equalsIgnoreCase("square")){
				addSquare(sqrTmp);
			}
			if(element.equalsIgnoreCase("name")){
				sqrTmp.setSquareName(tmpValue);
			}
			if(element.equalsIgnoreCase("number")){
				sqrTmp.setSquareNumber(Integer.parseInt(tmpValue));
			}
			if(element.equalsIgnoreCase("origin")){
				origen = getSquare(Integer.parseInt(tmpValue));
			}
			if(element.equalsIgnoreCase("d")){
				listaCasillasDst.add(getSquare(Integer.parseInt(tmpValue)));
			}
			if(element.equalsIgnoreCase("destiny")){
				connectSquare(origen,listaCasillasDst);
			}
			if(element.equalsIgnoreCase("map")){
				this.endDocument();
			}
			
		}
		
		public void characters(char[] ac, int i, int j) throws SAXException {
			tmpValue = new String(ac, i, j);
		}	
	}//END PRIVATE CLASS
	
}//END CLASS
